//{=============================================================================================================
//! @file SGLib.h
//! @brief SGLibrary3D - новая версия SGLib с поддержкой 3D.
//!
//! Билиотека была разработана для тех кому режет глаза обычный код OpenGL,@startcross юных подаванов@endcross,
//! и для последующего применения в различных проектов.
//!
//! $Author: Andrew Bezzubtsev(aandrew002) $
//! $Date: 22.01.2015 17:31 GTM +0400 $
//! $Version: 3.1 [X11]$
//! $Revision: 13 $
//! $Copyright: (C) 2015, Andrew Bezzubtsev. $
//!
//! Вы узрите в данной версии:
//! - OpenGL & X11 интеграция
//! - Работа с мышкой
//! - Аккуратный, прилизаный код {да-да он существует :)}
//! - Рисование в 3х пространствах.{Теперь оно тоже существует}
//! @warning Для корректной работы надо создать OpenGL проект
//!
//}=============================================================================================================

#ifndef SGLIB_H_INCLUDED
#define SGLIB_H_INCLUDED

#if defined (WIN32) || defined (__WIN32__) || defined(_WIN32) || defined(_WIN32_WINNT)
        #error ____________________________________________
        #error |                                          |
        #error | SGLib error:                             |
        #error | You are using Windows OS.                |
        #error | You must download other version of SGLib.|
        #error |__________________________________________|
#endif // defined

#if !defined (__cplusplus)
        #ifdef __GNUC__
        #error _______________________________________________________________
        #error |                                                             |
        #error | SGLib error:                                                |
        #error | You ought to use *.CPP | *.CC | *.CXX file format to compile|
        #error | your application.                                           |
        #error | Maybe you are using *.C file. Go to File -> Save as...      |
        #error | and save your file as *.CPP | *.CC | *.CXX file.            |
        #error |_____________________________________________________________|
        #endif
#endif // !defined

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/keysumdef.h>
#include <ncurses.h>

#include <GL/glx.h>
#include <GL/gl.h>
#include <GL/glu.h>

#ifndef check
    #if !defined(DEBUG)
        #define check(b) verified(b)

    #elif defined(NDEBUG) && !defined(NDEBUG)
        #define check(b) asserted(b)
    #endif
#endif // check

#ifndef asserted
    #define asserted(b) if(!b){printf("Error!\nLine: %d\nFunction: %s\nCall: %s\n", __LINE__, __PRETTY_FUNCTION__, #b); sgExit();}
#endif // asserted

#ifndef verified
    #define verified(b) if(!b){printf("Error!\n"); exit(0);}
#endif // verified

#ifndef MAX
    #define MAX(a, b) (a > b ? a : b)
#endif // MAX

#ifndef MIN
    #define MIN(a, b) (a < b ? a : b)
#endif // MIN

#ifndef ROUND
    #define ROUND(a) (int)(a + 0.5)
#endif // ROUND

struct POINT
{
        int x, y;

        POINT(int, int);
};

POINT::POINT(int x, int y)
{
        POINT::x = x;
        POINT::y = y;
}

struct POINT3D
{
        int x, y, z;

        POINT3D(int, int, int);
};

POINT3D::POINT3D(int x, int y, int z)
{
        POINT3D::x = x;
        POINT3D::y = y;
        POINT3D::z = z;
}


namespace SGWindow
{

        namespace _detail_
        {
            POINT pos                             = POINT(0, 0);
            POINT Size                            = POINT(0, 0);
            POINT WinSize                         = POINT(0, 0);

            const char DefWindowTitle [ ]         = "SGLib [X11]";

            bool buttons [255]                    = {};

            Display                 *display      = NULL;
            XVisualInfo             *VisInfo      = NULL;
            Colormap                CMap          = NULL;
            XSetWindowAttributes    SetWAttr      = NULL;
            GLXContext              Context       = NULL;
            XWindowAttributes       WndAttr       = NULL;
            XEvent                  Events        = NULL;

            Window                  Wnd           = NULL,
                                    Win           = NULL;

            GLint                   attrubites [] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None};
        }

        Display display()
        {
                return _detail_::display;
        }

        XVisualInfo VInfo()
        {
            return _detail_::VisInfo;
        }

        Colormap CMap()
        {
            return _detail_::CMap;
        }

        XSetWindowAttributes SetWAttr()
        {
            return _detail_::SetWAttr;
        }

        GLXContext Context()
        {
            return _detail_::Context;
        }

        XWindowAttributes WndAttr()
        {
            return _detail_::WndAttr;
        }

        XEvent Events()
        {
            return _detail_::Events;
        }

        Window RootWnd()
        {
            return _detail_::Wnd;
        }

        Window Win()
        {
            return _detail_::Win;
        }

        GLint* attributes()
        {
            return _detail_::attrubites;
        }

        POINT Position()
        {
                return _detail_::pos;
        }

        POINT Size()
        {
                return _detail::Size;
        }

        POINT WinSize()
        {
                return _detail_::WinSize;
        }

        char* Title()
        {
                return _detail_::DefWindowTitle;
        }

        bool Button(byte i)
        {
            return _detail_::buttons [i];
        }
}

namespace SGSys
{
        namespace _detail_
        {
                bool WindowRun   = false,
                     OpenGLRun   = false,
                     Rusificated = false,
                     DepthTest   = true;
        }

        bool WindowRun()
        {
            return _detail_::WindowRun;
        }

        bool OpenGLRun()
        {
            return _detail_::OpenGLRun;
        }

        bool Rusificated()
        {
            return _detail_::Rusificated;
        }

        bool DepthTest()
        {
            return _detail_::DepthTest;
        }
}

namespace SGUser
{
    bool Rusificate = true;
         DepthTest  = true;
}

void sgOK(bool TestWindow = false, bool TestGL = false);
void __sgSetWindowManagerHints__(Display* display, char* ClassName, char* argv[], int argc, Window window, int x, int y, int Width, int Height, char* WinTitle, char* PicTitle = WinTitle, Pixmap picto)

void sgOK(bool TestWindow, bool TestGL)
{
    if(TestWindow && !SGSys::WindowRun())                                      return false;
    if(TestGL     && !SGSys::OpenGLRun())                                      return false;
    if(SGSys::WindowRun && (SGWindow::Size().x < 1 || SGWindow::Size().y < 1)) return false;

    return true;
}
void __sgSetWindowManagerHints__(Display* display, char* ClassName, char* argv[], int argc, Window window, int x, int y, int Width, int Height, char* WinTitle, char* PicTitle, Pixmap picto)
{
        XSizeHints SizeHints;
        XWMHints   WM_HINTS;
        XClassHint ClassHint;

        XTextProperty Window, Pictogram;

        if(!XStringListToTextProperty (&ptrTitle, 1, &windowname ) || !XStringListToTextProperty (&ptrITitle, 1, &iconname))
        {
                printf("Global error!\nCan't create Window!\n");
                exit(1);
        }

        SizeHints.flags = PPosition | PSize;
        SizeHints.flags =

}

void sgCreateWindow(int width, int height, char name [ ], int x = 0, y = 0)
{
    check(!SGSys::WindowRun());

    SGWindow::_detail_::pos = {x, y};
    SGWindow::_detail_::Size = {width, height};

    SGWindow::_detail_::display = XOpenDisplay(NULL);

    check(SGWindow::display());

    SGWindow::_detail_::Wnd = DefaultRootWindow(SGWindow::display());
    SGWindow::_detail_::VisInfo = glXChooseVisual(SGWindow::display(), 0, SGWindow::attributes());
    check(SGWindow::VInfo());

    SGWindow::_detail_::CMap = XCreateColorMap(SGWindow::display(), SGWindow::Wnd(), SGWindow::VInfo()->visual, AllocNone);

    SGWindow::_detail_::SetWAttr.colormap = SGWindow::CMap();
    SGWindow::_detail_::SetWAttr.event_mask = ExprouseMask | KeyPressMask;

    SGWindow::_detail_::Win = XCreateWindow(SGWindow::display, SGWindow::RootWnd(), SGWindow::Position.x, SGWindow::Position.y, SGWindow::Size().x, SGWindow::Size().y, 0,
                                            SGWindow::VInfo()->depth, InputOutput, SGWindow::VInfo()->visual, CWColormap | CWEventMask, SGWindow::SetWAttr());

    check(SGWindow::Win());
    SGSys::_detail_::WindowRun = true;

    XMapWindow(SGWindow::display(), SGWindow::Win());
    XStoreName(SGWindow::display(), SGWindow::Win(), name);

    SGWindow::_detail_::Context = glXCreateContext(SGWindow::display(), SGWindow::VInfo(), NULL, GL_TRUE);
    glXMakeCurrent(SGWindow::display(), SGWindow::Win(), SGWindow::Context());

    if(SGSys::DepthTest()) sgRun3D();
    else                   sgRun2D();
}

void sgRun3D()
{
    glEnable(GL_DEPTH_TEST);
}

void sgRun2D()
{
    glDisable(GL_DEPTH_TEST);
}

void sgProcessMessages()
{
    asserted(SGSys::WindowRun());

    while(true)
    {
        XNextEvent(SGWindow::display(), &SGWindow::Events());

        switch(SGWindow::Events())
        {
            case Expose:
            {
                XGetWindowAttributes(SGWindow::display(), SGWindow::Win(), &SGWindow::attributes());
                glViewport(0, 0, SGWindow::attributes().width, SGWindow::attributes().height);
                sgUpdate();
            }
            break;

            case KeyPress:
            {
                  glXMakeCurrent(SGWindow::display(), None, NULL);
                  SGWindow::_detail_::buttons [SGWindow::attributes().]
            }
            break;
        }
    }
}

void sgExit()
{
    if(SGWindow::WindowRun())
    {
        if(SGSys::Rusificated()) printf("Нажмите любую клавишу чтобы продолжить...\n");
        else                     printf("Press any key to end executing...");

        while(true)
        {
            for(int i = 0; i < 255; i++)
            {
                if(SGWindow::Button(i))
                {
                    exit(0);
                }
            }
        }
    }
}

#endif //SGLIB_H_INCLUDED
